/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package onehander;

import java.awt.Color;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.PriorityQueue;
import javax.swing.table.DefaultTableModel;

/**
 *
 * @author John Skinner
 */
public class Typer extends javax.swing.JDialog {

    public static final int TOTALCHARS = 45;
    public static int TotalActualChars;
    HNode m_root;
    HNode m_current;
    int m_pressLevel;
    String m_lastChar;

    class HNode implements Comparable<HNode> {

        public HNode(String theChar, float theProb) {
            c = new HNode[4];
            hChar = theChar;
            prob = theProb;
            c[0] = c[1] = c[2] = c[3] = null;
            parent = null;
        }
        public HNode c[];
        public float prob;
        public String hChar;
        public HNode parent;

        @Override
        public int compareTo(HNode o) {
            if (prob < o.prob) {
                return -1;
            } else {
                if (prob > o.prob) {
                    return 1;
                } else {
                    return 0;
                }
            }
        }
    }
    DefaultTableModel m_hoffmanTableModel;
    HNode charSet[];

    /**
     * Creates new form Typer
     */
    public Typer(java.awt.Frame parent, boolean modal) {
        super(parent, modal);
        charSet = new HNode[TOTALCHARS];
        m_root = null;
        m_pressLevel = 0;
        m_current = null;
        m_lastChar = "";
        computeHoffmantable();
        initComponents();

    }
    /**
     * This method creates a new hoffman based coding for the one-handed
     * typer.
     */

    private void computeHoffmantable() {

        TotalActualChars = 0;

        PriorityQueue<HNode> m_queue;
        m_queue = new PriorityQueue();
        HNode a = new HNode("a", 0.08167f);
        charSet[TotalActualChars++] = a;
        m_queue.add(a);
        HNode b = new HNode("b", 0.01492f);
        charSet[TotalActualChars++] = b;
        m_queue.add(b);
        HNode c = new HNode("c", 0.02782f);
        charSet[TotalActualChars++] = c;
        m_queue.add(c);
        HNode d = new HNode("d", 0.04253f);
        charSet[TotalActualChars++] = d;
        m_queue.add(d);
        HNode e = new HNode("e", 0.12702f);
        charSet[TotalActualChars++] = e;
        m_queue.add(e);
        HNode f = new HNode("f", 0.02228f);
        charSet[TotalActualChars++] = f;
        m_queue.add(f);
        HNode g = new HNode("g", 0.02015f);
        charSet[TotalActualChars++] = g;
        m_queue.add(g);
        HNode h = new HNode("h", 0.06094f);
        charSet[TotalActualChars++] = h;
        m_queue.add(h);
        HNode i = new HNode("i", 0.06966f);
        charSet[TotalActualChars++] = i;
        m_queue.add(i);
        HNode j = new HNode("j", 0.00153f);
        charSet[TotalActualChars++] = j;
        m_queue.add(j);
        HNode k = new HNode("k", 0.00772f);
        charSet[TotalActualChars++] = k;
        m_queue.add(k);
        HNode l = new HNode("l", 0.04025f);
        charSet[TotalActualChars++] = l;
        m_queue.add(l);
        HNode m = new HNode("m", 0.02406f);
        charSet[TotalActualChars++] = m;
        m_queue.add(m);
        HNode n = new HNode("n", 0.06749f);
        charSet[TotalActualChars++] = n;
        m_queue.add(n);
        HNode o = new HNode("o", 0.07507f);
        charSet[TotalActualChars++] = o;
        m_queue.add(o);
        HNode p = new HNode("p", 0.01929f);
        charSet[TotalActualChars++] = p;
        m_queue.add(p);
        HNode q = new HNode("q", 0.00095f);
        charSet[TotalActualChars++] = q;
        m_queue.add(q);
        HNode r = new HNode("r", 0.05987f);
        charSet[TotalActualChars++] = r;
        m_queue.add(r);
        HNode s = new HNode("s", 0.06327f);
        charSet[TotalActualChars++] = s;
        m_queue.add(s);
        HNode t = new HNode("t", 0.09056f);
        charSet[TotalActualChars++] = t;
        m_queue.add(t);
        HNode u = new HNode("u", 0.02758f);
        charSet[TotalActualChars++] = u;
        m_queue.add(u);
        HNode v = new HNode("v", 0.00978f);
        charSet[TotalActualChars++] = v;
        m_queue.add(v);
        HNode w = new HNode("w", 0.02360f);
        charSet[TotalActualChars++] = w;
        m_queue.add(w);
        HNode x = new HNode("x", 0.00150f);
        charSet[TotalActualChars++] = x;
        m_queue.add(x);
        HNode y = new HNode("y", 0.01974f);
        charSet[TotalActualChars++] = y;
        m_queue.add(y);
        HNode z = new HNode("z", 0.00074f);
        charSet[TotalActualChars++] = z;
        m_queue.add(z);
        HNode space = new HNode(" ", 0.129f);
        charSet[TotalActualChars++] = space;
        m_queue.add(space);
        HNode period = new HNode(".", 0.08f);
        charSet[TotalActualChars++] = period;
        m_queue.add(period);
        HNode one = new HNode("1", 0.08f);
        charSet[TotalActualChars++] = one;
        m_queue.add(one);
        HNode two = new HNode("2", 0.08f);
        charSet[TotalActualChars++] = two;
        m_queue.add(two);
        HNode three = new HNode("3", 0.08f);
        charSet[TotalActualChars++] = three;
        m_queue.add(three);
        HNode four = new HNode("4", 0.08f);
        charSet[TotalActualChars++] = four;
        m_queue.add(four);
        HNode five = new HNode("5", 0.08f);
        charSet[TotalActualChars++] = five;
        m_queue.add(five);
        HNode six = new HNode("6", 0.08f);
        charSet[TotalActualChars++] = six;
        m_queue.add(six);
        HNode seven = new HNode("7", 0.08f);
        charSet[TotalActualChars++] = seven;
        m_queue.add(seven);
        HNode eight = new HNode("8", 0.08f);
        charSet[TotalActualChars++] = eight;
        m_queue.add(eight);
        HNode nine = new HNode("9", 0.08f);
        charSet[TotalActualChars++] = nine;
        m_queue.add(nine);
        HNode zero = new HNode("0", 0.08f);
        charSet[TotalActualChars++] = zero;
        m_queue.add(zero);
        HNode backspace = new HNode("BS",0.081f);
        m_queue.add(backspace);
        charSet[TotalActualChars++] = backspace;
        HNode newMsg = new HNode("NM",0.081f);
        charSet[TotalActualChars++] = newMsg;
        m_queue.add(newMsg);
        

        while (m_queue.size() > 1) {
            HNode intermediate = new HNode("", 1.0f);
            intermediate.c[0] = m_queue.remove();
            intermediate.c[1] = m_queue.remove();
            if (m_queue.size() > 0) {
                intermediate.c[2] = m_queue.remove();
            }
            if (m_queue.size() > 0) {
                intermediate.c[3] = m_queue.remove();
            }
            float totalProb = 0.0f;
            for (int iv = 0; iv < 4; iv++) {
                if (intermediate.c[iv] != null) {
                    totalProb += intermediate.c[iv].prob;
                    intermediate.c[iv].parent = intermediate;
                }
            }
            intermediate.prob = totalProb;
            m_queue.add(intermediate);
        }
        String fingerNames[] = {"V", "4", "3", "2"};
        StringBuffer code = new StringBuffer(20);
        m_root = m_queue.peek();
                m_hoffmanTableModel = new DefaultTableModel(TotalActualChars, 3);

        for (int ci = 0; ci < TotalActualChars; ci++) {
            HNode node = charSet[ci];

            m_hoffmanTableModel.setValueAt(node.hChar, ci, 0);
            code.setLength(0);

            while (node != null) {
                // find index from parent
                HNode par = node.parent;
                if (par != null) {

                    int nodeBranch = -1;
                    for (int branch = 0; branch < 4; branch++) {
                        if (par.c[branch] != null) {
                            if (par.c[branch] == node) {
                                nodeBranch = branch;
                            }
                        }
                    }
                    code.append(fingerNames[nodeBranch]);
                }
                node = par;

            }
            String codeRev = code.reverse().toString();
            m_hoffmanTableModel.setValueAt(codeRev, ci, 1);
            m_hoffmanTableModel.setValueAt(Float.toString(charSet[ci].prob), ci, 2);
        }
    }

    String nextTok(int f) {
        if (m_current == null) {
            m_current = m_root;
        }
        HNode next = m_current.c[f];
        boolean hasChar;
        hasChar = next.hChar != null;
        if (hasChar) {
            hasChar = next.hChar.length() > 0;
        }
        
        if (hasChar) {
            m_current = null;
            return next.hChar;
        } else {
            m_current = next;
        }
        return "";
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        m_output = new javax.swing.JTextField();
        panel1 = new java.awt.Panel();
        m_ring = new java.awt.Label();
        m_index = new java.awt.Label();
        m_pointer = new java.awt.Label();
        m_thumb = new java.awt.Label();
        jScrollPane1 = new javax.swing.JScrollPane();
        m_hoffmanTable = new javax.swing.JTable();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);

        org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, panel1, org.jdesktop.beansbinding.ELProperty.create("${visible}"), this, org.jdesktop.beansbinding.BeanProperty.create("focusable"));
        bindingGroup.addBinding(binding);

        addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                formKeyPressed(evt);
            }
            public void keyReleased(java.awt.event.KeyEvent evt) {
                formKeyReleased(evt);
            }
        });

        m_output.setEditable(false);
        m_output.setFont(new java.awt.Font("Courier New", 1, 36)); // NOI18N
        m_output.setToolTipText("");

        panel1.setMinimumSize(new java.awt.Dimension(152, 100));
        panel1.setLayout(new java.awt.GridLayout(1, 0));

        m_ring.setAlignment(java.awt.Label.CENTER);
        m_ring.setFont(new java.awt.Font("Courier New", 1, 48)); // NOI18N
        m_ring.setText("2");
        panel1.add(m_ring);

        m_index.setAlignment(java.awt.Label.CENTER);
        m_index.setFont(new java.awt.Font("Courier New", 1, 48)); // NOI18N
        m_index.setName(""); // NOI18N
        m_index.setText("3");
        panel1.add(m_index);

        m_pointer.setAlignment(java.awt.Label.CENTER);
        m_pointer.setFont(new java.awt.Font("Courier New", 1, 48)); // NOI18N
        m_pointer.setName(""); // NOI18N
        m_pointer.setText("4");
        panel1.add(m_pointer);
        m_pointer.getAccessibleContext().setAccessibleName("F1");
        m_pointer.getAccessibleContext().setAccessibleDescription("");

        m_thumb.setAlignment(java.awt.Label.CENTER);
        m_thumb.setFont(new java.awt.Font("Courier New", 1, 48)); // NOI18N
        m_thumb.setText("V");
        panel1.add(m_thumb);

        jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
        jScrollPane1.setHorizontalScrollBar(null);

        m_hoffmanTable.setFont(new java.awt.Font("Courier New", 0, 14)); // NOI18N
        m_hoffmanTable.setModel(m_hoffmanTableModel);
        m_hoffmanTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
        m_hoffmanTable.setAutoscrolls(false);
        m_hoffmanTable.setRequestFocusEnabled(false);
        m_hoffmanTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane1.setViewportView(m_hoffmanTable);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(panel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(m_output)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 483, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(m_output, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(panel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 717, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        bindingGroup.bind();

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void formKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_formKeyPressed
        this.m_pressLevel++;
        if (evt.getKeyChar() == 'v') {
            m_thumb.setBackground(Color.GRAY);
        }
        if (evt.getKeyChar() == '4') {
            m_pointer.setBackground(Color.GRAY);
        }
        if (evt.getKeyChar() == '3') {
            m_index.setBackground(Color.GRAY);
        }
        if (evt.getKeyChar() == '2') {
            m_ring.setBackground(Color.GRAY);
        }
    }//GEN-LAST:event_formKeyPressed

    private void formKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_formKeyReleased
        // TODO add your handling code here:
        m_pressLevel--;
        int index = -1;
        if (evt.getKeyChar() == 'v') {
            index = 0;
            m_thumb.setBackground(Color.white);
        }
        if (evt.getKeyChar() == '4') {
            index = 1;
            m_pointer.setBackground(Color.white);
        }
        if (evt.getKeyChar() == '3') {
            index = 2;
            m_index.setBackground(Color.white);
        }
        if (evt.getKeyChar() == '2') {
            index = 3;
            m_ring.setBackground(Color.white);
        }
        if (m_pressLevel == 0) {
            String v = nextTok(index);
            if (v.length() > 0) {
                String pv = m_output.getText();
                if (v.equals("BS")) {
                    String pvNew = pv.substring(0, pv.length()-1);
                    m_output.setText(pvNew);
                } else {
                    if (v.equals("NM")) {
                        String pvNew = "";
                        m_output.setText(pvNew);
                    }
                    else {
                    String pvNew = pv.concat(v);
                    m_output.setText(pvNew);
                    }
                }
            }
        }
    }//GEN-LAST:event_formKeyReleased

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(Typer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(Typer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(Typer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(Typer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the dialog */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                Typer dialog = new Typer(new javax.swing.JFrame(), true);
                dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                    @Override
                    public void windowClosing(java.awt.event.WindowEvent e) {
                        System.exit(0);
                    }
                });
                dialog.setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable m_hoffmanTable;
    private java.awt.Label m_index;
    private javax.swing.JTextField m_output;
    private java.awt.Label m_pointer;
    private java.awt.Label m_ring;
    private java.awt.Label m_thumb;
    private java.awt.Panel panel1;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration//GEN-END:variables
}
